using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace ShaderLab
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class ShaderLabOld : Microsoft.Xna.Framework.Game
    {
        // Graphics
        GraphicsDeviceManager graphics;


        // View
        SampleArcBallCamera camera;

        Matrix projection;

        Matrix view;


        // Space
        Matrix world;


        #region Lighting
        //List<PointLight> pointLights;
        #endregion


        #region Meshes
        Model[] sampleMeshes;

        int activeMesh = 0;
        #endregion


        #region Textures
        Texture2D texture;
        #endregion


        #region Effects
        Effect flatShaded;

        Effect color_FlatShaded;

        Effect ambDirVertex;

        Effect color_AmbDirVertex;

        Effect colorTexture_AmbDirVertex;

        Effect color_AmbDir_DifSpecVertex;

        Effect color_AmbDir_Pixel;

        Effect color_AmbDir_DifSpecPixel;

        Effect materialShader_3_0;
        #endregion


        #region Input State
        private GamePadState lastGpState;

        private KeyboardState lastKbState;
        #endregion


        public ShaderLabOld()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            // Set the backbuffer up
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;

            // Set up the shader requirements
            graphics.MinimumVertexShaderProfile = ShaderProfile.VS_3_0;
            graphics.MinimumPixelShaderProfile = ShaderProfile.PS_3_0;

            graphics.SynchronizeWithVerticalRetrace = false;
            IsFixedTimeStep = false;
        }

        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        protected override void LoadContent()
        {
            // load textures
            texture = Content.Load<Texture2D>("Textures/Clouds");


            // load meshes
            sampleMeshes = new Model[5];
            sampleMeshes[0] = Content.Load<Model>("Models/Cube");
            sampleMeshes[1] = Content.Load<Model>("Models/SphereHighPoly");
            sampleMeshes[2] = Content.Load<Model>("Models/SphereLowPoly");
            sampleMeshes[3] = Content.Load<Model>("Models/Cylinder");
            sampleMeshes[4] = Content.Load<Model>("Models/Cone");


            // load effects
            flatShaded = Content.Load<Effect>("Effects/FlatShaded");

            color_FlatShaded = Content.Load<Effect>("Effects/Color_FlatShaded");

            ambDirVertex = Content.Load<Effect>("Effects/AmbDirVertex");

            color_AmbDirVertex = Content.Load<Effect>("Effects/Color_AmbDirVertex");

            colorTexture_AmbDirVertex = Content.Load<Effect>("Effects/ColorTexture_AmbDirVertex");

            color_AmbDir_DifSpecVertex = Content.Load<Effect>("Effects/Color_AmbDir_DifSpecVertex");

            color_AmbDir_Pixel = Content.Load<Effect>("Effects/Color_AmbDir_Pixel");

            color_AmbDir_DifSpecPixel = Content.Load<Effect>("Effects/Color_AmbDir_DifSpecPixel");

            materialShader_3_0 = Content.Load<Effect>("Effects/MaterialShader_3_0");


            // set view
            camera = new SampleArcBallCamera(SampleArcBallCameraMode.RollConstrained);
            camera.Distance = 12;
            camera.OrbitRight(MathHelper.Pi);
            camera.OrbitUp(0.2f);

            projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4 * graphics.GraphicsDevice.Viewport.AspectRatio * 3 / 4, graphics.GraphicsDevice.Viewport.AspectRatio, 0.1f, 1000f);

            // set world
            world = Matrix.Identity;
        }

        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }


        


        protected void HandleInput(GameTime gameTime)
        {
            GamePadState gpState = GamePad.GetState(PlayerIndex.One);
            KeyboardState kbState = Keyboard.GetState();

            //check for exit
            if ((gpState.Buttons.Back == ButtonState.Pressed) ||
                kbState.IsKeyDown(Keys.Escape))
            {
                Exit();
            }

            //Handle inputs for the sample camera
            camera.HandleDefaultGamepadControls(
                gpState, gameTime);
            camera.HandleDefaultKeyboardControls(
                kbState, gameTime);

            float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

            //Handle input for selecting meshes
            if (((gpState.Buttons.X == ButtonState.Pressed) &&
                (lastGpState.Buttons.X == ButtonState.Released)) ||
                (kbState.IsKeyDown(Keys.Tab) && lastKbState.IsKeyUp(Keys.Tab)))
            {
                //switch the active mesh
                activeMesh = (activeMesh + 1) % sampleMeshes.Length;
            }


            //handle mesh rotation inputs
            float dx =
                SampleArcBallCamera.ReadKeyboardAxis(kbState, Keys.Left, Keys.Right) +
                gpState.ThumbSticks.Left.X;
            float dy =
                SampleArcBallCamera.ReadKeyboardAxis(kbState, Keys.Down, Keys.Up) +
                gpState.ThumbSticks.Left.Y;

            //apply mesh rotation to world matrix
            if (dx != 0)
            {
                world = world * Matrix.CreateFromAxisAngle(camera.Up,
                    elapsedTime * dx);
            }
            if (dy != 0)
            {
                world = world * Matrix.CreateFromAxisAngle(camera.Right,
                    elapsedTime * -dy);
            }

            lastGpState = gpState;
            lastKbState = kbState;
        }
        
        protected override void Update(GameTime gameTime)
        {
            HandleInput(gameTime);

            view = camera.ViewMatrix;



            base.Update(gameTime);
        }

        
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            //always set the shared effects parameters
            //SetSharedEffectParameters();

            //finally, draw the mesh itself
            world.Translation = Vector3.Zero;
            DrawSampleMesh(sampleMeshes[activeMesh]);

            world.Translation = new Vector3(1, 0, 0);
            DrawSampleMesh(sampleMeshes[activeMesh]);
            world.Translation = new Vector3(2, 0, 0);
            DrawSampleMesh(sampleMeshes[activeMesh]);
            world.Translation = new Vector3(3, 0, 0);
            DrawSampleMesh(sampleMeshes[activeMesh]);
            world.Translation = new Vector3(4, 0, 0);
            DrawSampleMesh(sampleMeshes[activeMesh]);
            world.Translation = new Vector3(5, 0, 0);
            DrawSampleMesh(sampleMeshes[activeMesh]);
            world.Translation = new Vector3(0, 1, 0);
            DrawSampleMesh(sampleMeshes[activeMesh]);
            world.Translation = new Vector3(0, 2, 0);
            DrawSampleMesh(sampleMeshes[activeMesh]);
            world.Translation = new Vector3(0, 3, 0);
            DrawSampleMesh(sampleMeshes[activeMesh]);
            world.Translation = new Vector3(0, 4, 0);
            DrawSampleMesh(sampleMeshes[activeMesh]);
            world.Translation = new Vector3(0, 5, 0);
            DrawSampleMesh(sampleMeshes[activeMesh]);


            base.Draw(gameTime);
        }


        public void DrawSampleMesh(Model model)
        {
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (ModelMeshPart meshPart in mesh.MeshParts)
                {
                    //set the vertex source to the mesh's vertex buffer
                    graphics.GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, meshPart.StreamOffset, meshPart.VertexStride);

                    //set the vertex delclaration
                    graphics.GraphicsDevice.VertexDeclaration = meshPart.VertexDeclaration;
                    //Console.WriteLine(meshPart.VertexDeclaration.GetVertexElements().Length);

                    //set the current index buffer to the sample mesh's index buffer
                    graphics.GraphicsDevice.Indices = mesh.IndexBuffer;
                    
                    // use the current effect
                    //Effect effect = flatShaded;
                    //Effect effect = vertexShaded;
                    //Effect effect = color_AmbDirVertex;
                    //Effect effect = color_AmbDir_Pixel;
                    //Effect effect = color_AmbDir_DifSpecPixel;
                    Effect effect = materialShader_3_0;
                    effect.Parameters["World"].SetValue(world);
                    effect.Parameters["Projection"].SetValue(projection);
                    effect.Parameters["View"].SetValue(view);
                    //effect.Parameters["AmbientColor"].SetValue(new Vector4(0.2f, 0.2f, 0.2f, 1.0f));
                    //effect.Parameters["LightColor"].SetValue(Color.CornflowerBlue.ToVector4());
                    //Vector3 lightDirec = new Vector3(-1, -1, -1);
                    //lightDirec.Normalize();
                    //effect.Parameters["LightDirection"].SetValue(lightDirec);
                    //effect.Parameters["LightPosition"].SetValue(new Vector3(30f, 30f, 30f));
                    //effect.Parameters["SpecularColor"].SetValue(Vector4.One);
                    //effect.Parameters["SpecularPower"].SetValue(16);
                    //effect.Parameters["SpecularIntensity"].SetValue(1);
                    effect.Parameters["CameraPosition"].SetValue(camera.Position);
                    //effect.Parameters["Texture"].SetValue(texture);

                    effect.Parameters["AmbientLight"].SetValue(new Vector4(0.0f, 0.0f, 0.0f, 1.0f));

                    effect.Parameters["Material"].StructureMembers["DiffuseColor"].SetValue(new Vector4(1.0f, 1.0f, 1.0f, 1.0f));

                    effect.Parameters["PointLights"].Elements[0].StructureMembers["Color"].SetValue(new Vector4(1.0f, 1.0f, 1.0f, 1.0f));
                    effect.Parameters["PointLights"].Elements[0].StructureMembers["Position"].SetValue(new Vector4(0.0f, 0.0f, 3.0f, 1f));
                    effect.Parameters["PointLights"].Elements[0].StructureMembers["FallOff"].SetValue(2f);
                    effect.Parameters["PointLights"].Elements[0].StructureMembers["Range"].SetValue(8f);

                    effect.Parameters["PointLights"].Elements[1].StructureMembers["Color"].SetValue(new Vector4(1.0f, 0.0f, 1.0f, 1.0f));
                    effect.Parameters["PointLights"].Elements[1].StructureMembers["Position"].SetValue(new Vector4(8.0f, 0.0f, 0.0f, 1f));
                    effect.Parameters["PointLights"].Elements[1].StructureMembers["FallOff"].SetValue(2f);
                    effect.Parameters["PointLights"].Elements[1].StructureMembers["Range"].SetValue(8f);

                    effect.Parameters["PointLights"].Elements[2].StructureMembers["Color"].SetValue(new Vector4(0.0f, 1.0f, 1.0f, 1.0f));
                    effect.Parameters["PointLights"].Elements[2].StructureMembers["Position"].SetValue(new Vector4(0.0f, 8.0f, 0.0f, 1f));
                    effect.Parameters["PointLights"].Elements[2].StructureMembers["FallOff"].SetValue(2f);
                    effect.Parameters["PointLights"].Elements[2].StructureMembers["Range"].SetValue(8f);

                    //effect.Parameters["AmbientLight"].StructureMembers["Color"].SetValue(new Vector4(0.2f, 0.2f, 0.2f, 1.0f));
                    



                    //at this point' we're ready to begin drawing
                    //To start using any effect, you must call Effect.Begin
                    //to start using the current technique (set in LoadGraphicsContent)
                    effect.Begin(SaveStateMode.None);

                    //now we loop through the passes in the teqnique, drawing each one in order
                    for (int i = 0; i < effect.CurrentTechnique.Passes.Count; i++)
                    {
                        //EffectPass.Begin will update the device to begin using the state information defined in the current pass
                        effect.CurrentTechnique.Passes[i].Begin();

                        //sampleMesh contains all of the information required to draw
                        //the current mesh
                        graphics.GraphicsDevice.DrawIndexedPrimitives(
                            PrimitiveType.TriangleList, meshPart.BaseVertex, 0,
                            meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);

                        //EffectPass.End must be called when the effect is no longer needed
                        effect.CurrentTechnique.Passes[i].End();
                    }

                    //Likewise, Effect.End will end the current technique
                    effect.End();
                }
            }
        }

    }
}
